{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Gestures;

interface

uses Classes, Colour, AHMTypes;

const
  GESTURE_POINT_RADIUS = 0.05;
  GESTURE_TRAIL_MAX_POINTS = 500;
  GESTURE_TRAIL_LIFETIME = 50;

type
  TAHMMousePoint = class
  private
    FX: Single;
    FY: Single;
    FTime: Integer;
  public
    constructor Create(X, Y: Single; Time: Integer);
    property X: Single read FX;
    property Y: Single read FY;
    property Time: Integer read FTime;
  end;

  TAHMMouseTrail = class
  private
    FAge: Integer;
    FPoints: TList;
  protected
    function GetCount: Integer;
    function GetPoint(Index: Integer): TAHMMousePoint;
  public
    constructor Create;
    destructor Destroy; override;
    property Count: Integer read GetCount;
    property Points[Index: Integer]: TAHMMousePoint read GetPoint; default;
    procedure AddPoint(X, Y: Single);
    procedure DeletePoint(Index: Integer);
    procedure Clear;
    procedure BeginTrail;
    procedure EndTrail;
    procedure FadeTrail;
  end;

  TAHMGestureManager = class
  private
    FTrail: TAHMMouseTrail;
    FColour: TAHMCanvasColour;
    FGamma: TAHMCanvasColour;
  protected
  public
    constructor Create;
    destructor Destroy; override;
    procedure MouseDown(X, Y: Single);
    procedure MouseMove(X, Y: Single; Down: Boolean);
    procedure MouseUp(X, Y: Single);
    procedure Render;
  end;

implementation

uses Screen, Canvas;

// TAHMMousePoint

constructor TAHMMousePoint.Create(X, Y: Single; Time: Integer);
begin
  inherited Create;

  FX := X;
  FY := Y;
  FTime := Time;
end;

// TAHMMouseTrail

constructor TAHMMouseTrail.Create;
begin
  inherited;

  FPoints := TList.Create;
end;

destructor TAHMMouseTrail.Destroy;
begin
  Clear;
  FPoints.Free;

  inherited;
end;

function TAHMMouseTrail.GetCount: Integer;
begin
  Result := FPoints.Count;
end;

function TAHMMouseTrail.GetPoint(Index: Integer): TAHMMousePoint;
begin
  Result := TAHMMousePoint(FPoints[Index]);
end;

procedure TAHMMouseTrail.AddPoint(X, Y: Single);
var
  Point: TAHMMousePoint;
begin
  Point := TAHMMousePoint.Create(X, Y, FAge);
  FPoints.Add(Point);
end;

procedure TAHMMouseTrail.DeletePoint(Index: Integer);
begin
  TAHMMousePoint(FPoints[Index]).Free;
  FPoints.Delete(Index);
end;

procedure TAHMMouseTrail.Clear;
var
  i: Integer;
begin
  for i := Pred(FPoints.Count) downto 0 do
    TAHMMousePoint(FPoints[i]).Free;
  FPoints.Clear;
end;

procedure TAHMMouseTrail.BeginTrail;
begin
  Clear;
  FAge := 0;
end;

procedure TAHMMouseTrail.EndTrail;
begin
//TODO: send points for gesture recognition
end;

procedure TAHMMouseTrail.FadeTrail;
var
  i: Integer;
begin
  // Limit maximum number of points
  while FPoints.Count > GESTURE_TRAIL_MAX_POINTS do
    FPoints.Delete(0);

  // Increment age and kill old points
  Inc(FAge);

  for i := Pred(FPoints.Count) downto 0 do
    if (FAge - Points[i].Time) > GESTURE_TRAIL_LIFETIME then
      DeletePoint(i);
end;

// TAHMGestureManager

constructor TAHMGestureManager.Create;
begin
  inherited;

  FTrail := TAHMMouseTrail.Create;
  FGamma := TAHMCanvasColour.CreateARGB(255, 255, 255, 255);
  FColour := TAHMCanvasColour.CreateARGB(255, 255, 0, 0);
end;

destructor TAHMGestureManager.Destroy;
begin
  FTrail.Free;
  FGamma.Free;
  FColour.Free;

  inherited;
end;

procedure TAHMGestureManager.MouseDown(X, Y: Single);
begin
  // Start drawing a new trail
  FTrail.BeginTrail;
  FTrail.AddPoint(X, Y);
end;

procedure TAHMGestureManager.MouseMove(X, Y: Single; Down: Boolean);
begin
  // Extend current trail if mouse button held down
  if Down then FTrail.AddPoint(X, Y);
end;

procedure TAHMGestureManager.MouseUp(X, Y: Single);
begin
  // Finish drawing the trail
  FTrail.EndTrail;
end;

procedure TAHMGestureManager.Render;
var
  i: Integer;
  Point: TAHMMousePoint;
begin
  // Fade trail to gradually remove old points
  FTrail.FadeTrail;

  // Draw gesture trail on main viewport canvas
  if FTrail.Count > 1 then
    with ScreenInstance.Viewport do
    begin
      Canvas.UnlockRotation;
      Canvas.UnSetRotation;
      Canvas.PenColour := FColour;
      Canvas.PenWidth := GESTURE_POINT_RADIUS;
      Canvas.BrushColour := FColour;

      for i := 0 to Pred(FTrail.Count) do
      begin
        Point := FTrail.Points[i];

        // Render line segment
        if i = 0 then Canvas.MoveTo(Point.X, Point.Y)
                 else Canvas.LineTo(Point.X, Point.Y, FGamma);
      end;
    end;
end;


end.
